use std::str::FromStr;

use crate::http::{error::HttpParseError, utils, FetchFromBytes, FromBytes, HttpMethod, HttpUri, HttpVersion};

#[derive(Debug, PartialEq, Eq)]
pub struct HttpRequestLine {
    pub method: HttpMethod,
    pub uri: HttpUri,
    pub version: HttpVersion,
}

impl FromStr for HttpRequestLine {
    type Err = HttpParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // [Method] [path] [version]
        let mut parts = s.split_ascii_whitespace();
        
        let method = parts.next()
            .ok_or_else(|| HttpParseError::InvalidRequest("No method found".into()))?;
        let method: HttpMethod = FromStr::from_str(method)?;

        let uri = parts.next()
            .ok_or_else(|| HttpParseError::InvalidRequest("No uri found".into()))?;
        let uri: HttpUri = FromStr::from_str(uri)?;

        let version = parts.next()
            .ok_or_else(|| HttpParseError::InvalidRequest("No version found".into()))?;
        let version: HttpVersion = FromStr::from_str(version)?;
        
        Ok(Self { method, uri, version })   
    }
}

impl FetchFromBytes for HttpRequestLine {
    type Err = HttpParseError;
    fn fetch_from_bytes(bytes: &[u8]) -> Result<(&[u8], Self), Self::Err> {
        match utils::find_bytes(&bytes, b"\r\n") {
            Some(line_end) => {
                let line: HttpRequestLine = FromBytes::from_bytes(&bytes[..line_end])?;
                Ok((&bytes[line_end+2..], line))
            }
            None => Err(HttpParseError::InvalidRequest("No '\\r\\n' found in request line".into())),
        }
    }
}

impl ToString for HttpRequestLine {
    fn to_string(&self) -> String {
        let mut result = String::new();

        result.push_str(&self.method.to_string());
        result.push(' ');

        result.push_str(&self.uri.to_string());
        result.push(' ');

        result.push_str(&self.version.to_string());
        result.push_str("\r\n");

        result
    }
}
